home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
016a
/
gofer221.zip
/
MAIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-11-20
|
30KB
|
1,001 lines
/* --------------------------------------------------------------------------
* main.c: Copyright (c) Mark P Jones 1991. All rights reserved.
* See goferite.h for details and conditions of use etc...
* Gofer version 2.21 November 1991
*
* Last updated 12/11/91 mpj
*
* Command interpreter
* ------------------------------------------------------------------------*/
#include "prelude.h"
#include "storage.h"
#include "command.h"
#include "connect.h"
#include "errors.h"
#include <setjmp.h>
#include <ctype.h>
#if TURBOC
#include <sys\stat.h>
#include <time.h>
#endif
#if UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
/* --------------------------------------------------------------------------
* Local function prototypes:
* ------------------------------------------------------------------------*/
static Void local initialise Args((Int,String []));
static Void local interpreter Args((Int,String []));
static Void local changeDir Args((Void));
static Void local set Args((Void));
static Void local toggleSet Args((Char,Bool));
static Void local togglesIn Args((Bool));
static Void local optionInfo Args((Void));
static Void local processOption Args((String));
static Int local argToInt Args((String *));
static Void local load Args((Void));
static Void local project Args((Void));
static Void local loadProject Args((String));
static Void local clearProject Args((Void));
static Void local addScriptName Args((String));
static Void local readScripts Args((Int));
static Void local addScript Args((String,Long));
static Void local forgetScriptsFrom Args((Module));
static Void local editor Args((Void));
static Void local find Args((Void));
static Void local runEditor Args((Void));
static Void local setLastEdit Args((String,Int));
static Void local evaluator Args((Void));
static Void local stopAnyPrinting Args((Void));
static Void local showtype Args((Void));
static Void local listNames Args((Void));
static Void local help Args((Void));
static Void local guidance Args((Void));
static Void local forHelp Args((Void));
static Void local failed Args((Void));
static String local strCopy Args((String));
static Int local substr Args((String,String));
/* --------------------------------------------------------------------------
* Local data areas:
* ------------------------------------------------------------------------*/
static String scriptName[NUM_MODULES]; /* Script file names */
static time_t lastChange[NUM_MODULES]; /* Time of last change to file */
static Int numScripts; /* Number of scripts loaded */
static Int namesUpto; /* Number of script names set */
static String currProject = 0; /* Name of current project file */
static Bool projectLoaded = FALSE; /* TRUE => project file loaded */
static String scriptFile; /* Name of current script (if any) */
static String lastEdit = 0; /* Name of file to edit (if any) */
static Int lastLine = 0; /* Editor line number (if possible)*/
static Bool printing = FALSE; /* TRUE => currently printing value*/
static Bool addType; /* TRUE => print type with value */
static String prompt; /* Prompt string */
static Bool showStats = TRUE; /* TRUE => print stats after eval */
/* --------------------------------------------------------------------------
* Gofer entry point:
* ------------------------------------------------------------------------*/
Void main Args((Int, String [])); /* now every func has a prototype */
Void main(argc,argv)
int argc;
char *argv[]; {
CStackBase = &argc; /* Save stack base for use in gc */
/* The startup banner now includes my name. Gofer is provided free of */
/* charge. I ask however that you show your appreciation for the many */
/* hours of work involved by retaining my name in the banner. Thanks! */
printf("Gofer Version 2.21 Copyright (c) Mark P Jones 1991\n\n");
fflush(stdout);
interpreter(argc,argv);
printf("[Leaving Gofer]\n");
everybody(EXIT);
exit(0);
}
/* --------------------------------------------------------------------------
* Initialisation, interpret command line args and read prelude:
* ------------------------------------------------------------------------*/
static Void local initialise(argc,argv)/* interpreter initialisation */
Int argc;
String argv[]; {
Module i;
String proj = 0;
lastEdit = 0;
scriptFile = 0;
numScripts = 0;
namesUpto = 1;
scriptName[0] = strCopy(fromEnv("GOFER","prelude"));
prompt = strCopy("?");
for (i=1; i<argc; ++i) /* process command line arguments */
if (strcmp(argv[i],"+")==0 && i+1<argc)
if (proj) {
ERROR(0) "Muliple project filenames on command line"
EEND;
}
else
proj = argv[++i];
else
addScriptName(argv[i]);
everybody(INSTALL);
if (proj) {
if (namesUpto>1)
fprintf(stderr,
"\nUsing project file, ignoring additional filenames\n");
loadProject(strCopy(proj));
}
readScripts(0);
}
/* --------------------------------------------------------------------------
* main read-eval-print loop, with error trapping:
* ------------------------------------------------------------------------*/
static jmp_buf catch_error; /* jump buffer for error trapping */
static Void local interpreter(argc,argv)/* main interpreter loop */
Int argc;
String argv[]; {
Int errorNumber = setjmp(catch_error);
breakOn(TRUE); /* enable break trapping */
if (numScripts==0) { /* only succeeds on first time, */
if (errorNumber) /* before prelude has been loaded */
internal("Unable to load prelude");
initialise(argc,argv);
forHelp();
}
for (;;) {
dropModulesFrom(numScripts-1); /* remove partially loaded scripts */
everybody(RESET); /* not counting prelude as a module */
printf("%s ",prompt); /* print prompt string */
fflush(stdout);
consoleInput();
switch (readCommand()) {
case EDIT : editor();
break;
case FIND : find();
break;
case LOAD : clearProject();
forgetScriptsFrom(1);
load();
break;
case ALSO : clearProject();
forgetScriptsFrom(numScripts);
load();
break;
case RELOAD : readScripts(1);
break;
case PROJECT: project();
break;
case EVAL : evaluator();
break;
case TYPEOF : showtype();
break;
case NAMES : listNames();
break;
case HELP : help();
break;
case BADCMD : guidance();
break;
case SET : set();
break;
case SYSTEM : shellEsc(readLine());
break;
case CHGDIR : changeDir();
break;
case QUIT : return;
case NOCMD : break;
}
}
}
static Void local changeDir() { /* change directory */
extern int chdir Args((String));
String s = readFilename();
if (s && chdir(s)) {
ERROR(0) "Unable to change to directory \"%s\"", s
EEND;
}
}
Void errHead(l) /* print start of error message */
Int l; {
failed(); /* failed to reach target ... */
stopAnyPrinting();
fprintf(errorStream,"ERROR");
if (scriptFile) {
fprintf(errorStream," \"%s\"", scriptFile);
setLastEdit(scriptFile,l);
if (l) fprintf(errorStream," (line %d)",l);
scriptFile = 0;
}
fprintf(errorStream,": ");
fflush(errorStream);
}
Void errFail() { /* terminate error message and */
putc('\n',errorStream); /* produce exception to return to */
fflush(errorStream); /* main command loop */
longjmp(catch_error,1);
}
Void errAbort() { /* altern. form of error handling */
failed(); /* used when suitable error message*/
stopAnyPrinting(); /* has already been printed */
errFail();
}
Void internal(msg) /* handle internal error */
String msg; {
fflush(stdout);
printf("\nINTERNAL ERROR: %s\n",msg);
everybody(EXIT);
exit(1);
}
Int breakHandler() { /* respond to break interrupt */
breakOn(TRUE);
printf("{Interrupted!}\n");
everybody(BREAK);
failed();
stopAnyPrinting();
fflush(stdout);
longjmp(catch_error,1);
return(0); /*NOTREACHED*/
}
/* --------------------------------------------------------------------------
* Setting of command line options:
* ------------------------------------------------------------------------*/
static Void local set() { /* change command line options from*/
String s; /* Gofer command line */
if (s=readFilename()) {
do {
if (s[0]=='+' || s[0]=='-')
processOption(s);
else {
ERROR(0) "Option string must begin with `+' or `-'"
EEND;
}
} while (s=readFilename());
}
else
optionInfo();
}
static struct { /* List of command line toggles */
char c;
String description;
Bool *flag;
} toggle[] = {
{'s', "Print no. reductions/cells after eval", &showStats},
{'t', "Print type after evaluation", &addType},
{'d', "Show dictionary values in output exprs",&showDicts},
{'f', "Terminate evaluation on first error", &failOnError},
{'g', "Print no. cells recovered after gc", &gcMessages},
{'c', "Test conformality for pattern bindings",&useConformality},
{'l', "Treat input files as literate scripts", &literateScripts},
{'e', "Warn about errors in literate scripts", &literateErrors},
{'i', "Apply fromInteger to integer literals", &coerceNumLiterals},
{'o', "Optimise use of (&&) and (||)", &andorOptimise},
{'u', "Catch ambiguously typed top-level vars",&catchAmbigs},
{'a', "Use any evidence, not nec. best", &anyEvidence},
{'E', "Fail silently if evidence not found", &silentEvFail},
{0, 0, 0}
};
static Void local toggleSet(c,state) /* Set command line toggle */
Char c;
Bool state; {
Int i;
for (i=0; toggle[i].c; ++i)
if (toggle[i].c == c) {
*toggle[i].flag = state;
return;
}
ERROR(0) "Unknown toggle `%c'", c
EEND;
}
static Void local togglesIn(state) /* Print current list of toggles in*/
Bool state; { /* given state */
Int count = 0;
Int i;
for (i=0; toggle[i].c; ++i)
if (*toggle[i].flag == state) {
if (count==0)
putchar(state ? '+' : '-');
putchar(toggle[i].c);
count++;
}
if (count>0)
putchar(' ');
}
static Void local optionInfo() { /* Print information about command */
static String fmts = "%-5s%s\n"; /* line settings */
static String fmtc = "%-5c%s\n";
Int i;
printf("Groups of options begin with +/- to turn options on/off resp.\n");
printf("\nTOGGLES:\n");
for (i=0; toggle[i].c; ++i)
printf(fmtc,toggle[i].c,toggle[i].description);
printf("\nOTHER OPTIONS: (leading + or - makes no difference)\n");
printf(fmts,"hnum","Set heap size (cannot be changed within Gofer)");
printf(fmts,"pstr","Set prompt string to str");
printf(fmts,"xnum","Set maximum depth for evidence search");
printf("\nCurrent settings: ");
togglesIn(TRUE);
togglesIn(FALSE);
printf("-h%d -p%s -x%d\n",heapSize,prompt,maxEvidLevel);
}
static Void local processOption(s) /* process option string s */
String s; {
Bool state = (s[0]=='+' ? TRUE : FALSE);
while (*++s)
switch (*s) {
case 'p' : if (s[1]) {
if (prompt) free(prompt);
prompt = strCopy(s+1);
}
return;
case 'h' : if (heapBuilt()) {
ERROR(0) "Cannot change heap size"
EEND;
}
heapSize = argToInt(&s);
if (heapSize<MINIMUMHEAP)
heapSize = MINIMUMHEAP;
#if MAXIMUMHEAP > 0
else if (heapSize>MAXIMUMHEAP)
heapSize = MAXIMUMHEAP;
#endif
break;
case 'x' : maxEvidLevel = argToInt(&s);
break;
default : toggleSet(*s,state);
break;
}
}
static Int local argToInt(sp) /* read integer from argument str */
String *sp; {
Int num = 0;
while (isascii((*sp)[1]) && isdigit((*sp)[1])) {
num = 10*num + (*(++*sp) - '0');
}
return num;
}
/* --------------------------------------------------------------------------
* Loading and removal of script files:
* ------------------------------------------------------------------------*/
static Void local load() { /* read filenames from command line */
String s; /* and add to list of files waiting */
/* to be read */
while (s=readFilename())
addScriptName(s);
readScripts(1);
}
static Void local project() { /* read list of file names from */
String s; /* project file */
if ((s=readFilename()) || currProject) {
if (!s)
s = strCopy(currProject);
else if (readFilename()) {
ERROR(0) "Too many project files"
EEND;
}
else
s = strCopy(s);
}
else {
ERROR(0) "No project filename specified"
EEND;
}
loadProject(s);
readScripts(1);
}
static Void local loadProject(s) /* Load project file */
String s; {
clearProject();
currProject = s;
projInput(currProject);
scriptFile = currProject;
forgetScriptsFrom(1);
while (s=readFilename())
addScriptName(s);
if (namesUpto<=1) {
ERROR(0) "Empty project file"
EEND;
}
scriptFile = 0;
projectLoaded = TRUE;
}
static Void local clearProject() { /* clear name for current project */
if (currProject)
free(currProject);
currProject = 0;
projectLoaded = FALSE;
}
static Void local addScriptName(s) /* add script name to list of files */
String s; { /* to be read in ... */
if (s[0]=='-' || s[0]=='+')
processOption(s);
else if (namesUpto>=NUM_MODULES) {
ERROR(0) "Too many script files (maximum of %d allowed)",
NUM_MODULES
EEND;
}
else
scriptName[namesUpto++] = strCopy(s);
}
static Void local readScripts(first) /* reread current list of scripts, */
Int first; { /* loading everything after and */
static struct stat scbuf; /* including the first script which */
Module i; /* has been either changed of added */
for (i=first; i<namesUpto; ++i) {
stat(scriptName[i],&scbuf);
if (i<numScripts && scbuf.st_mtime!=lastChange[i]) {
dropModulesFrom(i-1); /* previously loaded file changed ?*/
numScripts = i;
}
if (i>=numScripts) { /* new script file to be read ? */
lastChange[i] = scbuf.st_mtime;
if (i>0) /* no new module for prelude */
startNewModule();
addScript(scriptName[i],((Long)scbuf.st_size));
numScripts++;
}
}
printf("\nGofer session for:");
if (projectLoaded)
printf(" (project: %s)",currProject);
for (i=0; i<numScripts; ++i)
printf("\n%s",scriptName[i]);
putchar('\n');
if (numScripts<=1)
lastEdit = 0;
}
static Void local addScript(fname,len) /* read single script file */
String fname; /* name of script file */
Long len; { /* length of script file */
scriptFile = fname;
printf("Reading script file \"%s\":\n",fname);
setLastEdit(fname,0);
parseScript(fname,len); /* process script file */
checkDefns();
typeCheckDefns();
compileDefns();
scriptFile = 0;
}
static Void local forgetScriptsFrom(scno)/* remove scripts from system */
Module scno; {
Module i;
for (i=scno; i<namesUpto; ++i)
if (scriptName[i])
free(scriptName[i]);
dropModulesFrom(scno-1); /* don't count prelude as module */
namesUpto = scno;
if (numScripts>0)
numScripts = scno;
}
/* --------------------------------------------------------------------------
* Access to external editor:
* ------------------------------------------------------------------------*/
static Void local editor() { /* interpreter-editor interface */
String newFile = readFilename();
if (newFile) {
setLastEdit(newFile,0);
if (readFilename()) {
ERROR(0) "Multiple filenames not permitted"
EEND;
}
}
runEditor();
readScripts(1); /* try to reload scripts after edit*/
}
static Void local find() { /* edit file containing definition */
String nm = readFilename(); /* of specified name */
if (!nm) {
ERROR(0) "No name specified"
EEND;
}
else if (readFilename()) {
ERROR(0) "Multiple names not permitted"
EEND;
}
else {
Name n;
startNewModule();
if (isNull(n = findName(findText(nm)))) {
ERROR(0) "No current definition for name \"%s\"", nm
EEND;
}
setLastEdit(scriptName[moduleThisName(n)],name(n).line);
runEditor();
readScripts(1);
}
}
static Void local runEditor() { /* run editor on file lastEdit at */
static char editorCmd[100]; /* line editLine */
String editor = fromEnv("EDITOR",DEF_EDITOR);
String editline = fromEnv("EDITLINE",DEF_EDITLINE);
Int l,f;
if (lastEdit && editline && lastLine
&& (l=substr("%d",editline))>=0
&& (f=substr("%s",editline))>=0)
if (l<f)
sprintf(editorCmd,editline,lastLine,lastEdit);
else
sprintf(editorCmd,editline,lastEdit,lastLine);
else if (editor)
if (lastEdit)
sprintf(editorCmd,"%s %s",editor,lastEdit);
else
sprintf(editorCmd,"%s",editor);
else {
ERROR(0) "No editor specified in environment variable EDITOR"
EEND;
}
if (shellEsc(editorCmd)) {
ERROR(0) "Editor terminated abnormally"
EEND;
}
}
static Void local setLastEdit(fname,line)/* keep name of last file to edit */
String fname;
Int line; {
if (lastEdit)
free(lastEdit);
lastEdit = strCopy(fname);
lastLine = line;
}
/* --------------------------------------------------------------------------
* Read and evaluate an expression:
* ------------------------------------------------------------------------*/
static Void local evaluator() { /* evaluate expr and print value */
Type type;
scriptFile = 0;
startNewModule(); /* Enables recovery of storage */
/* allocated during evaluation */
parseExp();
checkExp();
type = typeCheckExp();
if (whatIs(isPolyType(type) ? snd(type) : type)==QUAL) {
ERROR(0) "Unresolved overloading" ETHEN
ERRTEXT "\n*** type : " ETHEN ERRTYPE(type);
ERRTEXT "\n*** translation : " ETHEN ERREXPR(inputExpr);
ERRTEXT "\n"
EEND;
}
compileExp();
numCells = 0;
numReductions = 0;
numberGcs = 0;
printing = TRUE;
if (typeMatches(type,typeString))
outputString(stdout,graphForExp(),TRUE);
else if (typeMatches(type,typeDialogue))
dialogue(graphForExp());
else {
outputString(stdout,ap(ap(ap(namePrint,
mkInt(MIN_PREC)),
graphForExp()),
nameNil),TRUE);
if (addType) {
printf(" :: ");
printType(stdout,type);
}
}
stopAnyPrinting();
}
static Void local stopAnyPrinting() { /* terminate printing of expression,*/
if (printing) { /* after successful termination or */
printing = FALSE; /* runtime error (e.g. interrupt) */
putchar('\n');
if (showStats) {
#define plural(v) v, (v==1?"":"s")
printf("(%lu reduction%s, ",plural(numReductions));
printf("%lu cell%s",plural(numCells));
if (numberGcs>0)
printf(", %u garbage collection%s",plural(numberGcs));
printf(")\n");
#undef plural
}
fflush(stdout);
}
}
/* --------------------------------------------------------------------------
* Print type of input expression:
* ------------------------------------------------------------------------*/
static Void local showtype() { /* print type of expression (if any)*/
Cell type;
startNewModule(); /* Enables recovery of storage */
/* allocated during evaluation */
parseExp();
checkExp();
type = typeCheckExp();
printExp(stdout,inputExpr);
printf(" :: ");
printType(stdout,type);
putchar('\n');
}
/* --------------------------------------------------------------------------
* List all names currently in scope:
* ------------------------------------------------------------------------*/
static Void local listNames() { /* list names matching optional pat*/
String pat = readFilename();
List names = NIL;
Int width = getTerminalWidth() - 1;
Int count = 0;
Int termPos;
if (pat) /* First gather names to list */
do
names = addNamesMatching(pat,names);
while (pat=readFilename());
else
names = addNamesMatching((String)0,names);
if (isNull(names)) { /* Then print them out */
ERROR(0) "No names selected"
EEND;
}
for (termPos=0; nonNull(names); names=tl(names)) {
String s = textToStr(name(hd(names)).text);
Int l = strlen(s);
if (termPos+1+l>width) {
putchar('\n');
termPos = 0;
}
else if (termPos>0) {
putchar(' ');
termPos++;
}
printf("%s",s);
termPos += l;
count++;
}
printf("\n(%d names listed)\n", count);
}
/* --------------------------------------------------------------------------
* Print Menu of list of commands:
* ------------------------------------------------------------------------*/
static Void local help() {
printf("LIST OF COMMANDS: Any command may be abbreviated to :c where\n");
printf("c is the first character in the full name.\n\n");
printf(":set <options> set command line options\n");
printf(":set help on command line options\n");
printf(":? display this list of commands\n");
printf("<expr> evaluate expression\n");
printf(":type <expr> print type of expression\n");
printf(":names [pat] list names currently in scope\n");
printf(":load <filenames> load scripts from specified files\n");
printf(":load clear all files except prelude\n");
printf(":also <filenames> read additional script files\n");
printf(":reload repeat last load command\n");
printf(":project <filename> use project file\n");
printf(":edit <filename> edit file\n");
printf(":edit edit last file\n");
printf(":find <name> edit file containing definition of name\n");
printf(":! command shell escape\n");
printf(":cd dir change directory\n");
printf(":quit exit Gofer interpreter\n");
}
Char cmdFirstChar = ':';
struct cmd commands[] = {
{":?", HELP}, {":type", TYPEOF}, {":load", LOAD},
{":also", ALSO}, {":reload", RELOAD}, {":project", PROJECT},
{":edit", EDIT}, {":find", FIND}, {":names", NAMES},
{":set", SET}, {":quit", QUIT}, {":cd", CHGDIR},
{":!", SYSTEM}, {"", EVAL},
{0,0}
};
static Void local guidance() {
printf("Command not recognised. ");
forHelp();
}
static Void local forHelp() {
printf("Type :? for help\n");
}
/* --------------------------------------------------------------------------
* Display progress towards goal:
* ------------------------------------------------------------------------*/
static Target currTarget;
static Int currPos;
static Int maxPos;
static Bool aiming = FALSE;
Void setGoal(what, t) /* Set goal for what to be t */
String what;
Target t; {
currTarget = (t?t:1);
currPos = strlen(what);
maxPos = getTerminalWidth() - 1;
aiming = TRUE;
printf("%s",what);
fflush(stdout);
}
Void soFar(t) /* Indicate progress towards goal */
Target t; { /* has now reached t */
Int newPos = (Int)((maxPos * ((long)t))/currTarget);
if (newPos>maxPos)
newPos = maxPos;
if (newPos>currPos) {
do
putchar('.');
while (newPos>++currPos);
fflush(stdout);
}
}
Void done() { /* Goal has now been achieved */
while (maxPos>currPos++)
putchar('.');
putchar('\n');
fflush(stdout);
aiming = FALSE;
}
static Void local failed() { /* Goal cannot be reached due to */
if (aiming) { /* errors */
aiming = FALSE;
putchar('\n');
fflush(stdout);
}
}
/* --------------------------------------------------------------------------
* Send message to each component of system:
* ------------------------------------------------------------------------*/
Void everybody(what) /* send command `what' to each component of*/
Int what; { /* system to respond as appropriate ... */
machdep(what); /* The order of calling each component is */
storage(what); /* important for the INSTALL command */
input(what);
staticAnalysis(what);
typeChecker(what);
compiler(what);
machine(what);
builtIn(what);
}
/* --------------------------------------------------------------------------
* Read value from environment variable:
* ------------------------------------------------------------------------*/
String fromEnv(var,def) /* return value of: */
String var; /* environment variable named by var */
String def; { /* or: default value given by def */
extern char *getenv Args((char *));
String s = getenv(var);
return (s ? s : def);
}
/* --------------------------------------------------------------------------
* String manipulation routines:
* ------------------------------------------------------------------------*/
static String local strCopy(s) /* make malloced copy of a string */
String s; {
char *t,*r;
if ((t=(char *)malloc(strlen(s)+1))==0) {
ERROR(0) "String storage space exhausted"
EEND;
}
for (r=t; *r++ = *s++;)
;
return t;
}
static Int local substr(s1,s2) /* find posn of substring s1 in s2 */
String s1, s2; { /* (naive implementation) */
String t;
for (t=s2; *t; t++) {
Int i = 0;
while (s1[i] && s1[i]==t[i])
i++;
if (s1[i]=='\0')
return t-s2;
}
return (-1);
}
#define USE_REGEXP 0
#if USE_REGEXP
/* --------------------------------------------------------------------------
* String matching using regular expressions:
*
* Relies on the regexp.h header file provided by many UNIX systems.
* If you do not have this file for your your system/compiler you will
* have to set #define USE_REGEXP 0 above and use the simple string
* matching routine below.
* ------------------------------------------------------------------------*/
#define INIT register char *sp = instring;
#define GETC() (*sp++)
#define PEEKC() (*sp)
#define UNGETC(c) (--sp)
#define RETURN(c) return c
#define ERROR(c) regErr(c)
#define ESIZE 200
static Void local regErr Args((int)); /* report error in regular expr */
static Void local regErr(errorcode)
int errorcode; {
if (errorcode==50) {
ERROR(0) "Regular expression too complex"
EEND;
}
else {
ERROR(0) "Error in regular expression"
EEND;
}
}
#include <regexp.h>
Bool stringMatch(pat,str) /* match string against pattern */
String pat;
String str; {
char expbuf[ESIZE];
compile(pat,expbuf,&expbuf[ESIZE],'\0');
return step(str,expbuf);
}
#else
/* --------------------------------------------------------------------------
* A simple string matching routine
* `*' matches any sequence of zero or more characters
* `?' matches any single character exactly
* `@str' matches the string str exactly (ignoring any special chars)
* `\c' matches the character c only (ignoring special chars)
* c matches the character c only
* ------------------------------------------------------------------------*/
static Void local patternError Args((String));
static Void local patternError(s) /* report error in pattern */
String s; {
ERROR(0) "%s in pattern", s
EEND;
}
Bool stringMatch(pat,str) /* match string against pattern */
String pat;
String str; {
for (;;)
switch (*pat) {
case '\0' : return (*str=='\0');
case '*' : do {
if (stringMatch(pat+1,str++))
return TRUE;
} while (*str);
pat++;
break;
case '?' : if (*str++=='\0')
return FALSE;
pat++;
break;
case '[' : { Bool found = FALSE;
while (*++pat!='\0' && *pat!=']')
if (!found && ( pat[0] == *str ||
(pat[1] == '-' &&
pat[2] != ']' &&
pat[2] != '\0' &&
pat[0] <= *str &&
pat[2] >= *str)))
found = TRUE;
if (*pat != ']')
patternError("missing `]'");
if (!found)
return FALSE;
pat++;
str++;
}
break;
case '\\' : if (*++pat == '\0')
patternError("extra trailing `\\'");
/*fallthru!*/
default : if (*pat++ != *str++)
return FALSE;
break;
}
}
#endif
/*-------------------------------------------------------------------------*/